package cn.lix.sc.rpc.client.impl.rpc;

import cn.lix.sc.rpc.impl.common.annotations.RpcClient;
import cn.lix.sc.rpc.impl.common.utils.SpringContextUtil;
import feign.Logger;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import feign.slf4j.Slf4jLogger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 增强的RPC客户端Bean处理器，支持多种内容类型和文件流处理
 *
 * @author lix
 * @date 2024
 */
@Configuration
public class EnhancedRpcClientBeanProcessor implements EmbeddedValueResolverAware, BeanPostProcessor, ImportAware {

    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());

    private StringValueResolver stringValueResolver;
    private Set<String> locationPatterns = new HashSet<>(8);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        try {
            proxyEnhancedRemote(bean);
        } catch (IllegalAccessException e) {
            throw new BeanCreationException("增强RPC客户端注入失败: " + beanName, e);
        }
        return bean;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.stringValueResolver = resolver;
    }

    private boolean placeholder(String value) {
        return value.startsWith(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX);
    }

    /**
     * 代理增强的远程服务
     */
    private void proxyEnhancedRemote(Object bean) throws IllegalAccessException {
        Class<?> beanClass = bean.getClass();
        if (locationPatterns.isEmpty() || locationPatterns.stream().noneMatch(e -> beanClass.getName().contains(e))) {
            return;
        }

        Field[] fields = beanClass.getDeclaredFields();
        for (Field field : fields) {
            RpcClient rpcClient = field.getAnnotation(RpcClient.class);
            if (rpcClient == null) {
                continue;
            }

            if (StringUtils.isEmpty(rpcClient.serverName())) {
                throw new IllegalAccessException(bean.getClass().getName().concat(".").concat(field.getName()).concat("注解RpcClient服务名不能为空"));
            }

            field.setAccessible(true);

            // 根据是否启用增强功能选择代理类型
            Object proxy = Proxy.newProxyInstance(
                    beanClass.getClassLoader(),
                    new Class[]{field.getType()},
                    new EnhancedJDKRemoteServiceProxy(
                            transformServerName(rpcClient.serverName()),
                            field.getType(),
                            rpcClient.beanName(),
                            rpcClient.connectTimeout(),
                            rpcClient.readTimeout(),
                            rpcClient.contentTypes(),
                            rpcClient.maxFileSize()
                    )
            );
            log.info("类[{}]注入增强RPC属性[{}]成功", bean.getClass().getName(), field.getName());

            field.set(bean, proxy);
            field.setAccessible(false);
        }
    }

    private String transformServerName(String server) {
        if (placeholder(server)) {
            return stringValueResolver.resolveStringValue(server);
        }
        return server;
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        Map<String, Object> defaultAttrs = importMetadata.getAnnotationAttributes(EnableRpcClient.class.getName(), true);
        if (defaultAttrs != null && !ObjectUtils.isEmpty(defaultAttrs.get("value"))) {
            Collections.addAll(locationPatterns, (String[]) defaultAttrs.get("value"));
        } else {
            this.locationPatterns.add(ClassUtils.getPackageName(importMetadata.getClassName()));
        }
    }

    @Bean(name = "enhancedClientSpringContextUtil")
    public SpringContextUtil enhancedSpringContextUtil() {
        return new SpringContextUtil();
    }

    /**
     * 关闭feign的失败重试功能
     */
    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetry() {
        return Retryer.NEVER_RETRY;
    }

    @Bean
    @ConditionalOnMissingBean
    public Logger.Level loggerLevel() {
        return Logger.Level.BASIC;
    }

    /**
     * 使用Jackson解码器替代FastJSON，增强配置
     */
    @Bean
    @ConditionalOnMissingBean
    public Decoder decoder() {
        com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
        // 忽略未知属性，防止ResponseEntity等复杂对象反序列化失败
        objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 允许空Bean
        objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        return new feign.jackson.JacksonDecoder(objectMapper);
    }

    /**
     * 使用Spring支持的表单编码器，完美支持multipart/form-data和JSON
     */
    @Bean
    @ConditionalOnMissingBean  
    public Encoder encoder() {
        return new feign.form.spring.SpringFormEncoder(new feign.jackson.JacksonEncoder());
    }

    @Bean
    @ConditionalOnMissingBean
    public Logger feignLogger() {
        return new Slf4jLogger();
    }

    @Bean
    @ConditionalOnMissingBean
    public FeignRequestInterceptor feignRequestInterceptor() {
        return new FeignRequestInterceptor();
    }
} 